use crate::http::client::error::Error;
use crate::http::client::watch;
use reqwest::Method;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use tokio::sync::RwLock;
use url::Url;
use wanmesh_common::dto::Response;
use wanmesh_common::error::ResponseError;

pub struct Auth {
    access_token: RwLock<String>,
}

impl Auth {
    pub fn new() -> Self {
        Self {
            access_token: RwLock::new("".to_string()),
        }
    }
    pub async fn try_get_access_token(&self) -> String {
        self.access_token.read().await.clone()
    }

    pub async fn get_access_token(&self) -> Result<String, Error> {
        let access_token = self.try_get_access_token().await;
        if access_token.is_empty() {
            return Err(Error::Other(anyhow::anyhow!("access_token为空")));
        }
        Ok(access_token)
    }

    pub async fn set_access_token(&self, access_token: String) {
        *self.access_token.write().await = access_token;
    }
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Setting {
    pub endpoint: Url,
}

#[derive(Debug, Clone, Default)]
pub struct CallOptions {
    pub auth_disable: bool,
}

pub struct Client {
    setting: Setting,
    inner: reqwest::Client,
    auth: Arc<Auth>,
}

impl Client {
    pub fn new(setting: Setting, client: reqwest::Client, auth: Arc<Auth>) -> Self {
        Self {
            setting,
            inner: client,
            auth,
        }
    }

    pub async fn call<T, R>(
        &self,
        path: &str,
        method: Method,
        request: T,
        opts: CallOptions,
    ) -> Result<R, Error>
    where
        T: Serialize,
        R: DeserializeOwned,
    {
        let u = self
            .setting
            .endpoint
            .join(path)
            .map_err(|e| anyhow::Error::new(e))?;

        let mut http_request = self.inner.request(method, u).json(&request);

        if !opts.auth_disable {
            let access_token = self.auth.get_access_token().await?;
            http_request = http_request.bearer_auth(access_token);
        }

        let http_response = http_request.send().await?;

        let status = http_response.status();
        if !status.is_success() {
            return match http_response.text().await {
                Ok(text) => Err(Error::Other(anyhow::anyhow!(
                    "http request failed, status: {}, body: {}",
                    status,
                    text
                ))),
                Err(e) => Err(Error::Other(anyhow::anyhow!(
                    "http request failed, status: {}, body_err: {}",
                    status,
                    e
                ))),
            };
        }

        let body = http_response.bytes().await?;

        let response: Response<serde_json::Value> =
            serde_json::from_slice(body.as_ref()).map_err(anyhow::Error::new)?;
        if !response.code.is_success() {
            return Err(Error::ResponseError(ResponseError {
                code: response.code,
                message: response.message,
                backtrace: Default::default(),
            }));
        }

        let response_data: R = serde_json::from_value(response.data).map_err(anyhow::Error::new)?;

        Ok(response_data)
    }

    pub async fn watch<T>(&self, path: &str, handler: T) -> Result<watch::WatchTask<T>, Error>
    where
        T: watch::TextMessageHandler + 'static,
    {
        let mut u = self
            .setting
            .endpoint
            .join(path)
            .map_err(anyhow::Error::new)?;

        match u.scheme() {
            "http" => {
                u.set_scheme("ws")
                    .map_err(|_| anyhow::anyhow!("设置ws错误"))?;
            }
            "https" => {
                u.set_scheme("wss")
                    .map_err(|_| anyhow::anyhow!("设置wss错误"))?;
            }
            _ => {
                return Err(Error::Other(anyhow::anyhow!(
                    "不支持的scheme={}",
                    u.scheme()
                )));
            }
        };

        let authorization = format!(
            "{}{}",
            wanmesh_common::auth::BEARER_PREFIX,
            self.auth.get_access_token().await?
        );

        u.query_pairs_mut()
            .append_pair(wanmesh_common::auth::AUTHORIZATION, authorization.as_str());

        let (websocket, _) = tokio_tungstenite::connect_async(u)
            .await
            .map_err(anyhow::Error::new)?;

        Ok(watch::WatchTask::new(websocket, handler))
    }
}
